#ifndef __PROTOCOL_HPP__
#define __PROTOCOL_HPP__
#include <iostream>
#include <stdlib.h>
#include <string>
#include <functional>
#include <vector>
#include <sstream>
#include <unistd.h>
#include <unordered_map>
#include <sys/wait.h>
#include <sys/stat.h>
#include <algorithm>
#include <fcntl.h>
#include <sys/sendfile.h>
#include "Util.hpp"
#include "Socket.hpp"
#define OK 200
#define NOT_FOUND 404        // 请求资源不存在
#define BAD_REQUEST 400
#define SERVER_ERROR 500
#define WEB_ROOT "./wwwroot"
#define ErrorDir "/errorPage/"
#define HOME_PAGE "index.html"
#define PAGE_404 "404.html"
#define PAGE_400 "400.html"
#define PAGE_500 "500.html"
#define HTTP_VERSION "HTTP/1.1"
#define RESP_SEP "\r\n"
class HttpRequest
{
public:
    // 原始数据
    HttpRequest() : _content_length(0), _cgi(false)
    {
    }
    bool _cgi;                                // 判断该请求是否需要cgi处理
    std::string _request_line;                // 请求行
    std::vector<std::string> _request_header; // 请求报头
    std::string _blank;                       // 空行
    std::string _request_body;                // 请求正文
    // 解析后的数据
    std::string _method;                                     // 请求方法
    std::string _uri;                                        // 请求uri
    std::string _version;                                    // http版本
    std::unordered_map<std::string, std::string> _header_kv; // 请求行kv结构
    int _content_length;
    // uri的构成是path?args
    std::string _path;
    std::string _args;
};
class HttpResponse
{
public:
    HttpResponse() : _status_code(OK), _fd(-1), _fd_size(0), _blank(RESP_SEP) {}
    std::string _status_line;
    std::vector<std::string> _response_header;
    std::string _blank;
    std::string _response_body;
    int _status_code;
    int _fd;      // 请求所需要打开的文件描述符
    int _fd_size; // 打开的文件的大小
};

class EndPoint
{
private:
    int ReadOneLine(std::string &out)
    {
        return _sock->Recv(std::bind(&Util::readline, _sock->Sockfd(), std::placeholders::_1), out);
    }
    void RecvHttpRequestLine()
    {
        int n = ReadOneLine(_req._request_line);
        if (n > 0)
        {
            _req._request_line.resize(_req._request_line.size() - 1);
        }
        else
        {
            _stop = true;
        } 
    }
    void ParseHttpRequestLine()
    {
        auto &line = _req._request_line;
        std::stringstream ss(line);
        ss >> _req._method >> _req._uri >> _req._version;
        DLOG("%s", _req._uri.c_str());
        auto &method = _req._method;
        std::transform(method.begin(), method.end(), method.begin(), ::toupper); // 大小写全部转化为大写
    }
    void RecvHttpRequestHeader()
    {
        std::string buffer;
        while (!_stop && buffer != "\n")
        {
            buffer.clear();
            if(ReadOneLine(buffer) > 0)
            {
                if (buffer == "\n")
                    break;
                buffer.resize(buffer.size() - 1);
                _req._request_header.push_back(buffer);
            }
            else
            {
                _stop = true;
            }
        }
        _req._blank = buffer;
    }
    void ParseHttpRequestHeader()
    {
        // 分隔符是冒号空格
        std::string sep = ": ";
        for (auto &iter : _req._request_header)
        {
            size_t pos = iter.find(sep);
            if (pos != std::string::npos)
            {
                std::string key;
                std::string value;
                Util::splitstring(iter, key, value, sep);
                _req._header_kv.insert(std::make_pair(key, value));
            }
        }
    }
    bool IsNeedRecvHttpRequestBody()
    {
        if (_req._method != "POST")
            return false;
        auto iter = _req._header_kv.find("Content-Length");
        if (iter == _req._header_kv.end())
            return false;
        _req._content_length = std::atoi(_req._header_kv["Content-Length"].c_str());
        return true;
    }
    void RecvHttpRequestBody() // 若有正文，读取正文
    {
        if (IsNeedRecvHttpRequestBody())
        {
            size_t length = _req._content_length;
            auto &body = _req._request_body;
            char ch;
            while (length)
            {
                size_t n = recv(_sock->Sockfd(), &ch, 1, 0);
                if (n > 0)
                {
                    body += ch;
                    length--;
                }
                else
                {
                    _stop = true;
                    break;
                }
            }
            DLOG("%s", body.c_str());
        }
    }
    int ProcessNonCgi(int size)
    {
        _resp._fd = open(_req._path.c_str(), O_RDONLY); // 只读方式打开
        if (_resp._fd >= 0)
        {
            _resp._status_line = HTTP_VERSION;                          // 协议版本
            _resp._status_line += ' ';                                  // 空格
            _resp._status_line += std::to_string(_resp._status_code);   // 状态码
            _resp._status_line += ' ';                                  // 空格
            _resp._status_line += Util::CodeToDesc(_resp._status_code); // 状态码描述
            _resp._status_line += RESP_SEP;                             // 分隔符
            _resp._fd_size = size;

            // 响应报头
            std::string resp_content_length = "Content-Length: ";
            resp_content_length += std::to_string(size);
            resp_content_length += RESP_SEP;
            _resp._response_header.push_back(resp_content_length);
            std::string resp_content_type = "Content-Type: ";
            std::string type = Util::SuffixToType(_req._path);
            if (type != "" && type != "Unkown")
                resp_content_type += type;
            else
                resp_content_type += "text/html";

            resp_content_type += RESP_SEP;
            _resp._response_header.push_back(resp_content_type);
            // 空行
            _resp._blank = "\r\n";
            return OK; // OK
        }
        return NOT_FOUND;
    }
    int ProcessCgi()
    {
        // 当前服务器进程不处理数据，而是交给cgi程序->exec
        // 不能直接替换，创建子进程
        // cgi程序要把处理完的数据交给http服务器，使用进程间通信(管道)
        DLOG("entance ProcessCgi");
        // 站在http服务器角度命名
        auto &bin = _req._path;
        auto &method = _req._method;
        auto &body = _req._request_body;
        auto &args = _req._args;
        auto &Length = _req._content_length;
        int code = OK;
        int output[2];
        int input[2];
        if (pipe(output) < 0)
        {
            ELOG("create output_pipe error!");
            return SERVER_ERROR;
        }
        if (pipe(input) < 0)
        {
            ELOG("create input_pipe error!");
            return SERVER_ERROR;
        }
        pid_t id = fork();
        if (id == 0)
        {
            // child
            DLOG("child close input and output pipe start...");
            close(input[0]); // input
            close(output[1]);

            DLOG("child close input and output pipe end...");

            // 导入method方法，用于区分POST和GET
            std::string method_env = "METHOD=";
            method_env += method;
            DLOG("%s", method_env.c_str());
            if (putenv(const_cast<char *>(method_env.c_str())) != 0)
            {
                ELOG("import env:method error!");
                exit(1);
            }
            std::string args_env = "ARGS=";
            std::string content_length_env = "CONTENT_LENGTH=" + std::to_string(Length);
            if (method == "GET")
            {
                // 导入环境变量方式传递参数
                args_env += args;
                if (putenv((char *)args_env.c_str()) != 0)
                {
                    ELOG("import env:args error!");
                    exit(1);
                }
            }
            else if (method == "POST")
            {
                // 导入环境变量Content_Length，用于确认有效载荷的长度
                if (putenv((char *)content_length_env.c_str()) != 0)
                {
                    ELOG("import env:content_length error!");
                    exit(1);
                }
            }
            DLOG("path:%s", bin.c_str());
            dup2(input[1], 1);
            dup2(output[0], 0);
            execl(bin.c_str(), bin.c_str(), nullptr);
            exit(1);
        }
        else if (id < 0)
        {
            return SERVER_ERROR;
        }
        else
        {
            // parent
            close(input[1]);
            close(output[0]);
            if (method == "POST")
            {
                // 管道方式传入参数
                const char *str = body.c_str();
                int size = 0;  // 本次写了多少数据？
                int total = 0; // 已经写了多少数据？
                while ((total < Length) && (size = write(output[1], str + total, body.size() - total) > 0))
                    total += size;
            }
            char ch = 0;
            while (read(input[0], &ch, 1) > 0)
                _resp._response_body += ch;
            int status;
            pid_t ret = waitpid(id, &status, 0);
            if (ret == id)
            {
                if (WIFEXITED(status))
                {
                    if (WEXITSTATUS(status) == 0)
                        code = OK;
                    else
                    {
                        code = SERVER_ERROR;
                    }
                }
                else
                {
                    code = SERVER_ERROR;
                }
            }
            else
            {
                code = SERVER_ERROR;
            }
            close(input[0]);
            close(output[1]);
        }
        return code;
    }
    void BuildOkResponse()
    {
        // Content-Type
        std::string line = "Content-Type: ";
        if (_req._path.rfind('.') != std::string::npos)
            line += Util::SuffixToType(_req._path);
        else
            line += "text/html";
        line += RESP_SEP;
        _resp._response_header.push_back(line);
        // Content-Length
        line = "Content-Length: ";
        if (_req._cgi)
        {
            line += std::to_string(_resp._response_body.size());
        }
        else
        {
            line += std::to_string(_resp._fd_size);
        }
        line += RESP_SEP;
        _resp._response_header.push_back(line);
    }
    void HandlerError(std::string page)
    {
        page = ErrorDir + page;
        page = WEB_ROOT + page;
        DLOG("HandlerError");
        _req._cgi = false;
        _resp._fd = open(page.c_str(), O_RDONLY);
        std::cout << _resp._fd << std::endl;
        std::cout << "page: " << page << std::endl;
        if (_resp._fd > 0)
        {
            struct stat st;
            stat(page.c_str(), &st);
            std::string line = "Content-Type: text/html";
            line += RESP_SEP;
            _resp._response_header.push_back(line);
            line = "Content-Length: ";
            line += std::to_string(st.st_size);
            line += RESP_SEP;
            _resp._fd_size = st.st_size;
            _resp._response_header.push_back(line);
        }
    }
    void BuildHttpResponsehelper()
    {
        // 根据错误码，构建响应
        auto &status_code = _resp._status_code;
        auto &status_line = _resp._status_line;
        status_line.clear(); // 为了保证不出错，最好清空一下
        //[HTTP版本] [状态码] [状态码描述]
        status_line += HTTP_VERSION;
        status_line += " ";
        status_line += std::to_string(status_code);
        status_line += " ";
        status_line += Util::CodeToDesc(status_code);
        status_line += RESP_SEP;
        // 根据状态码，打开某些文件
        switch (status_code)
        {
        case OK:
            BuildOkResponse();
            break;
        case NOT_FOUND:
            HandlerError(PAGE_404);
            break;
        case BAD_REQUEST:
            HandlerError(PAGE_400);
        case SERVER_ERROR:
            HandlerError(PAGE_500);
        default:
            //...
            break;
        }
    }

public:
    EndPoint(socket_ns::socket_ptr sock) : _sock(sock) , _stop(false)
    {
    }
    ~EndPoint()
    {
        _sock->Close();
    }

public:
    void RecvHttpRequest() // 读取并解析请求
    {
        RecvHttpRequestLine();
        if(!_stop) RecvHttpRequestHeader();
        if(!_stop) ParseHttpRequestLine();
        if(!_stop) ParseHttpRequestHeader();
        if(!_stop) RecvHttpRequestBody();
    }
    void BuildHttpResponse() // 构建响应
    {
        auto &code = _resp._status_code;
        if (_req._method != "GET" && _req._method != "POST")
        {
            // 非法请求
            code = BAD_REQUEST;
            BuildHttpResponsehelper();
            return;
        }

        // 对于post方法无需处理，因为已经保存在了请求正文中，对于get方法需要把uri进行特殊处理
        if (!Util::splitstring(_req._uri, _req._path, _req._args, "?"))
            _req._path = _req._uri;

        // 使用cgi机制的两种情况：GET方法并且uri中带了参数、POST方法
        if (_req._args.size() != 0 || _req._method == "POST")
        {
            _req._cgi = true;
        }
        // 如果用户未指定路径，则默认获取首页
        if (_req._path == "/")
            _req._path += HOME_PAGE;
        DLOG("path:%s", _req._path.c_str());
        // 添加path完整的路径
        if (_req._path[0] != '/')
            _req._path = '/' + _req._path;
        _req._path = WEB_ROOT + _req._path;
        // 判断文件是否存在，使用stat调用获取文件属性
        struct stat st;
        int size = 0;
        int n = stat(_req._path.c_str(), &st);
        if (n < 0)
        {
            // 若该文件不存在，则返回404响应
            code = NOT_FOUND;
            BuildHttpResponsehelper();
            return;
        }
        if (S_ISDIR(st.st_mode))
        {
            // 如果请求的是目录文件，默认返回首页
            _req._path = WEB_ROOT + std::string("/") + HOME_PAGE;
            stat(_req._path.c_str(), &st);
            _resp._fd = open(_req._path.c_str(), O_RDONLY); // 重新打开新文件
            _resp._fd_size = st.st_size;                    // 更新文件大小
        }
        else if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))
        {
            // 一定不是目录文件，并且该文件是可执行文件，使用cgi机制
            _req._cgi = true;
        }
        size = st.st_size;
        DLOG("path:%s", _req._path.c_str());
        if (_req._cgi)
        {
            code = ProcessCgi(); // 使用cgi处理
            std::cerr << "code : " << code << std::endl;
        }
        else
        {
            code = ProcessNonCgi(size); // 不使用cgi处理
        }
        BuildHttpResponsehelper();
        
    }
    void SendHttpResponse() // 发送响应
    {
        // 发送响应行
        send(_sock->Sockfd(), _resp._status_line.c_str(), _resp._status_line.size(), 0);
        // 发送响应报头
        for (auto &iter : _resp._response_header)
        {
            send(_sock->Sockfd(), iter.c_str(), iter.size(), 0);
        }
        // 发送空行
        send(_sock->Sockfd(), _resp._blank.c_str(), _resp._blank.size(), 0);
        if (_req._cgi)
        {
            auto &body = _resp._response_body;
            int size = 0;
            int total = 0;
            const char *start = body.c_str();
            while (total < body.size() && (size = send(_sock->Sockfd(), body.c_str() + total, body.size() - total, 0)) > 0)
            {
                total += size;
            }
        }
        else
        {
            std::cout << _resp._fd << std::endl;
            std::cout << _resp._fd_size << std::endl;
            sendfile(_sock->Sockfd(), _resp._fd, nullptr, _resp._fd_size);
            close(_resp._fd);
        }
        // 记得关闭文件描述符哦
    }
    bool Stop()
    {
        return _stop;
    }
private:
    socket_ns::socket_ptr _sock;
    HttpRequest _req;
    HttpResponse _resp;
    bool _stop; //判断读取出错，直接退出
};

class Entrance
{
public:
    static void *HandlerRequest(void *sock)
    {
        socket_ns::socket_ptr *spp = reinterpret_cast<socket_ns::socket_ptr *>(sock);
        socket_ns::socket_ptr sp = *spp;
        delete spp;
        EndPoint *ep = new EndPoint(sp);
        ep->RecvHttpRequest();
        if(!ep->Stop())
        {
            ILOG("Recv Not Error!");
            ep->BuildHttpResponse();
            ep->SendHttpResponse();
        }
        else 
        {
            ELOG("Recv Error!,Stop Build and Send");
        }
        delete ep;
        return nullptr;
    }
};

#endif
